//
//  YBBLE.m
//  YBBLE
//
//  Created by LPC on 2018/2/5.
//  Copyright © 2018年 audi. All rights reserved.
//  LPC
// https://www.cnblogs.com/gaozhang12345/p/5856728.html翻译

#import "YBBLE.h"
/**蓝牙UUID*/
NSString *const YBBLE_SERVICE_UUID = @"6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
NSString *const YBBLE_CHARACTER_WRITE_UUID = @"6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
NSString *const YBBLE_CHARACTER_READ_UUID = @"6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
// 永安站
NSString *const YBBLESTATION_SERVICE_UUID_1 = @"7E400001-B5A3-F393-E0A9-E50E24DCCA9E";
// 锁车器
NSString *const YBBLESTATION_SERVICE_UUID_2 = @"8E400001-B5A3-F393-E0A9-E50E24DCCA9E";

typedef void(^bleFinalTask)(void);

@interface YBBLE()<CBCentralManagerDelegate,CBPeripheralDelegate>
{
    __strong CBCentralManager *centralManager;
    NSMutableArray *_connectPeripherals;
    BOOL ifGuard;
    NSTimer *_taskTimer;
    NSTimer *_guradTimer;
}

@property (nonatomic ,copy ,readonly)NSString *peripheralName;
@property (nonatomic ,copy ,readonly)NSString *openLockMsg;
@property (nonatomic ,assign)NSInteger digits10;
//
@property (nonatomic ,assign)NSTimeInterval timeoutInterval;
@property (nonatomic ,strong)NSArray *searchServices;

@property (nonatomic ,assign)BOOL ifFirstValue;
@property (nonatomic ,copy)NSString *jointValue;
@property (nonatomic ,strong)CBPeripheral *yb_peripheral;
@property (nonatomic ,strong)CBCharacteristic *writeCharacteristic;
@property (nonatomic ,copy)bleFinalTask finalTask;

@end
@implementation YBBLE

static YBBLE* _instance = nil;

+ (instancetype)ble{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL]init];
    });
    return _instance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    return [self ble];
}

- (instancetype)init{
    if (self = [super init]) {
        centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey:@NO}];
        _connectPeripherals = [NSMutableArray array];
    }
    return self;
}

- (void)configParamsChannel:(YBBLEChannel)channel UseChannel:(NSString *)useChannel  peripheralName:(NSString *)peripheralName services:(NSArray<CBUUID *>*)services timeoutInterval:(NSTimeInterval)timeoutInterval delegate:(id<YBBLEDelegate>)delegate{
    @weakify(self)
    bleFinalTask task = ^{
        @strongify(self)
        NSLog(@"执行channel ---- %ld",(long)channel);
        [self reset];
        [self setDelegate:delegate];
        _channel = channel;
        _useChannel = useChannel;
        _peripheralName = peripheralName;
        if (timeoutInterval > 0) {
            _taskTimer = [NSTimer timerWithTimeInterval:timeoutInterval target:self selector:@selector(timeOut:) userInfo:nil repeats:NO];
            [[NSRunLoop currentRunLoop]addTimer:_taskTimer forMode:NSRunLoopCommonModes];
        }
        [centralManager scanForPeripheralsWithServices:services options:nil];
    };
    
    NSLog(@"channel来了 ---- %ld",(long)channel);
    if (!ifGuard) {
        task();
    }else{
        self.finalTask = task;
    }
}

- (CBCentralManagerState)state{
    return (CBCentralManagerState)centralManager.state;
}

#pragma mark - public

//清除
- (void)reset{
    [self cleanTimer];
    _peripheralName = nil;
    _openLockMsg = nil;
    _digits10 = 0;
    _ifFirstValue = YES;
    _jointValue = nil;
    _returnMsgFirst = nil;
    _returnMsgSecond = nil;
    _useChannel = nil;
    [self cancelAllPeripheralsConnection];
}

- (void)stopScan{
    [centralManager stopScan];
}

- (void)cleanTimer{
    [_taskTimer invalidate];
    _taskTimer = nil;
}

- (void)yb_listeningBLELock:(NSString *)lockName delegate:(id<YBBLEDelegate>)delegate{
    [self configParamsChannel:YBBLEChannel_listen UseChannel:nil peripheralName:lockName services: @[[CBUUID UUIDWithString:YBBLE_SERVICE_UUID]] timeoutInterval:0 delegate:delegate];
}

-(void)yb_listeningBLELockAllDelegate:(id<YBBLEDelegate>)delegate {
    [self configParamsChannel:YBBLEChannel_commom UseChannel:nil peripheralName:nil services:@[[CBUUID UUIDWithString:YBBLE_SERVICE_UUID]] timeoutInterval:0 delegate:delegate];
}

- (void)yb_searchBLELock:(NSString *)lockName UseChannel:useChannel timeOutIntervel:(NSTimeInterval)timeOutIntervel delegate:(id<YBBLEDelegate>)delegate{
    [self configParamsChannel:YBBLEChannel_search UseChannel:useChannel peripheralName:lockName services:@[[CBUUID UUIDWithString:YBBLE_SERVICE_UUID]] timeoutInterval:timeOutIntervel delegate:delegate];
}

/**
 查找永安站
 @param timeOutIntervel 超时时长
 */
- (void)yb_searchStationUseChannel:(NSString *)useChannel type:(int)type TimeOutIntervel:(NSTimeInterval)timeOutIntervel delegate:(id<YBBLEDelegate>)delegate{
    
    NSArray *temp;
    switch (type) {
        case 0:
            temp = @[[CBUUID UUIDWithString:YBBLESTATION_SERVICE_UUID_1]];
            break;
        case 1:
            temp = @[[CBUUID UUIDWithString:YBBLESTATION_SERVICE_UUID_2]];
            break;
        case 2:
            temp = @[[CBUUID UUIDWithString:YBBLESTATION_SERVICE_UUID_1],[CBUUID UUIDWithString:YBBLESTATION_SERVICE_UUID_2]];
            break;
        default:
            temp = @[[CBUUID UUIDWithString:YBBLESTATION_SERVICE_UUID_1],[CBUUID UUIDWithString:YBBLESTATION_SERVICE_UUID_2]];
            break;
    }
    
    [self configParamsChannel:YBBLEChannel_searchStaion UseChannel:useChannel peripheralName:nil services:temp timeoutInterval:timeOutIntervel delegate:delegate];
}

- (void)yb_openBLELock:(NSString *)lockName UseChannel:(NSString *)useChannel openLockMsg:(NSString *)openLockMsg timeOutValue:(NSTimeInterval)timeOutValue delegate:(id<YBBLEDelegate>)delegate{
    
    @weakify(self)
    bleFinalTask task = ^{
        @strongify(self)
        if (centralManager.state != CBCentralManagerStatePoweredOn) {
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLEdidFailStart:)]) {
                [self.delegate yb_BLEdidFailStart:self];
            }
            return;
        }
        [self reset];
        [self setDelegate:delegate];
        _channel = YBBLEChannel_openBLELock;
        _useChannel = useChannel;
        _peripheralName = lockName;
        _openLockMsg = openLockMsg;
        _taskTimer = [NSTimer timerWithTimeInterval:timeOutValue target:self selector:@selector(timeOut:) userInfo:nil repeats:NO];
        [[NSRunLoop currentRunLoop]addTimer:_taskTimer forMode:NSRunLoopCommonModes];
        
        if (_yb_peripheral && [_yb_peripheral.name isEqualToString:_peripheralName]) {
            [centralManager connectPeripheral:_yb_peripheral options:nil];
        }else{
            NSArray *servicesUUID = @[[CBUUID UUIDWithString:YBBLE_SERVICE_UUID]];
            [centralManager scanForPeripheralsWithServices:servicesUUID options:nil];
        }
    };
    
    if (!ifGuard) {
        task();
    }else{
        self.finalTask = task;
    }
}

- (void)clean{
    [centralManager stopScan];
    [self reset];
    [self setDelegate:nil];
    
}
#pragma mark - <链式编程语法>
- (YBBLE *(^)(YBBLEChannel))YBchannel{
    return ^(YBBLEChannel channel){
        self -> _channel = channel;
        return self;
    };
}

- (YBBLE *(^)(NSString *))YBLockName{
    return ^(NSString *lockName){
        self -> _openLockMsg = lockName;
        return self;
    };
}

- (YBBLE *(^)(void))begin{
    return ^{
        //dosomething
        return self;
    };
}

- (YBBLE *(^)(id<YBBLEDelegate>))YBDelegate{
    return ^(id delegate){
        self.delegate = delegate;
        return self;
    };
}

- (YBBLE *(^)(NSTimeInterval))YBTimeoutInterval{
    return ^(NSTimeInterval timeoutInterval){
        self.timeoutInterval = timeoutInterval;
        return self;
    };
}

- (YBBLE *(^)(NSString *))YBUseChannel{
    return ^(NSString *useChannel){
        self.useChannel = useChannel;
        return self;
    };
}

- (YBBLE *(^)(NSArray <CBUUID *>*))YBService{
    return ^(NSArray *services){
        self.searchServices = services;
        return self;
    };
}

//- (void)yb_searchBLELock:(NSString *)lockName timeOutIntervel:(NSTimeInterval)timeOutIntervel delegate:(id<YBBLEDelegate>)delegate{
//    [self configParamsChannel:YBBLEChannel_search peripheralName:lockName services:@[[CBUUID UUIDWithString:YBBLE_SERVICE_UUID]] timeoutInterval:timeOutIntervel delegate:delegate];
//}

#pragma mark - action
- (void)timeOut:(id)sender{
    if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLETaskTimerOut:)]) {
        [self.delegate yb_BLETaskTimerOut:self];
    }
}

- (void)guradTimeOut{
    NSLog(@"fun = %s line = %d",__func__,__LINE__);
    [_guradTimer invalidate];
    _guradTimer = nil;
    ifGuard = NO;
    if (self.finalTask) {
        self.finalTask();
        self.finalTask = nil;
    }
}

#pragma mark - CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
    if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didUpdateCentralManagerState:)]) {
        [self.delegate yb_BLE:self didUpdateCentralManagerState:(CBCentralManagerState)central.state];
    }
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
//    NSLog(@"fun = %s line = %d",__func__,__LINE__);
    NSLog(@"BLE 广播 主频道%ld --- 子频道 %@",(long)BLE.channel,BLE.useChannel);
    if (_channel == YBBLEChannel_commom) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didFindBLEAdvertisementData:RSSI:)]) {
            [self.delegate yb_BLE:self didFindBLEAdvertisementData:advertisementData RSSI:RSSI];
        }
        return;
    }
    if (_channel == YBBLEChannel_searchStaion) {
        [self cleanTimer];
        if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didFindBLEAdvertisementData:RSSI:)]) {
            [self.delegate yb_BLE:self didFindBLEAdvertisementData:advertisementData RSSI:RSSI];
        }
        [central stopScan];
        return;
    }

    NSString *lockName = advertisementData[@"kCBAdvDataLocalName"];
    if (![lockName isEqualToString:self.peripheralName]) {
        return;
    }
    _yb_peripheral = peripheral;

    switch (_channel) {
        case YBBLEChannel_openBLELock:
        {
            [central connectPeripheral:peripheral options:nil];
            [central stopScan];
        }
            break;
        case YBBLEChannel_search:
        {
            [self cleanTimer];
            [central stopScan];
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didFindBLEAdvertisementData:RSSI:)]) {
                [self.delegate yb_BLE:self didFindBLEAdvertisementData:advertisementData RSSI:RSSI];
            }
        }
            break;
        case YBBLEChannel_listen:
        {
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didFindBLEAdvertisementData:RSSI:)]) {
                [self.delegate yb_BLE:self didFindBLEAdvertisementData:advertisementData RSSI:RSSI];
            }
        }
            break;
        default:
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didFindBLEAdvertisementData:RSSI:)]) {
                [self.delegate yb_BLE:self didFindBLEAdvertisementData:advertisementData RSSI:RSSI];
            }
            break;
    }
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    [self addPeripheral:peripheral];
    peripheral.delegate = self;
    [peripheral discoverServices:@[[CBUUID UUIDWithString:YBBLE_SERVICE_UUID]]];
}

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    if (error) {
        NSLog(@"断开链接失败%@",peripheral.name);
        return;
    }
    NSLog(@"断开蓝牙成功");
    [self deletePeripheral:peripheral];
}

#pragma mark - CBPeripheralDelegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error
{
    for (CBService *service in peripheral.services) {
        if ([service.UUID.UUIDString isEqualToString: YBBLE_SERVICE_UUID]) {
            [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:YBBLE_CHARACTER_READ_UUID],[CBUUID UUIDWithString:YBBLE_CHARACTER_WRITE_UUID]] forService:service];
        }
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    [service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj.UUID.UUIDString isEqualToString:YBBLE_CHARACTER_READ_UUID]) {
            [peripheral setNotifyValue:YES forCharacteristic:obj];
        }else if ([obj.UUID.UUIDString isEqualToString:YBBLE_CHARACTER_WRITE_UUID]){
            self.writeCharacteristic = obj;
            [peripheral setNotifyValue:YES forCharacteristic:obj];
        }
    }];
}

//监听成功
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (characteristic == self.writeCharacteristic) {
        //creat protect
        ifGuard = YES;
        if (_guradTimer) {
            [_guradTimer invalidate];
            _guradTimer = nil;
        }
        _guradTimer = [NSTimer timerWithTimeInterval:10.f target:self selector:@selector(guradTimeOut) userInfo:nil repeats:NO];
        [[NSRunLoop currentRunLoop]addTimer:_guradTimer forMode:NSRunLoopCommonModes];
        //send
        [self peripheral:peripheral sendMessage:_openLockMsg forCharacteristic:characteristic];
    }
}

//蓝牙设备收到返回
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//    ff9aa91f0100e80002001200a1d0d954e7311617
    NSString *value = [self convertDataToHexStr:characteristic.value];
    _jointValue = [_jointValue stringByAppendingString:value];
    NSLog(@" value ----- %@",value);
    NSString *head = [value substringToIndex:6];
    if ([head isEqualToString:@"ff9aa9"]) {
        NSString *digits16 = [value substringWithRange:NSMakeRange(6, 2)];
        _digits10 = strtoul([digits16 UTF8String], 0, 16) * 2;
        NSLog(@"报文的位数 --- %ld",_digits10);
        if (_digits10 < 40 - 8) {
            _jointValue = [value substringToIndex:_digits10];
            _returnMsgFirst = _jointValue;
            _ifFirstValue = NO;
            [self cleanTimer];
            [self cancelAllPeripheralsConnection];
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didUpdataFirstValue:secondValue:isEnd:)]) {
                [self.delegate yb_BLE:self didUpdataFirstValue:_jointValue secondValue:nil isEnd:YES];
            }
        }else{
            _jointValue = value;
            _digits10 = _digits10 - (40 - 8);
        }
    }else{
        _jointValue = [_jointValue substringToIndex:_digits10 + 40];
        if (_ifFirstValue == YES) {
            _ifFirstValue = NO;
            _returnMsgFirst = _jointValue;
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didUpdataFirstValue:secondValue:isEnd:)]) {
                [self.delegate yb_BLE:self didUpdataFirstValue:_returnMsgFirst secondValue:nil isEnd:NO];
            }
        }else{
            _returnMsgSecond = _jointValue;
            if (self.channel == YBBLEChannel_openBLELock) {
                [self cleanTimer];
            }
            [self cancelAllPeripheralsConnection];
            if (self.delegate && [self.delegate respondsToSelector:@selector(yb_BLE:didUpdataFirstValue:secondValue:isEnd:)]) {
                 [self.delegate yb_BLE:self didUpdataFirstValue:_returnMsgFirst secondValue:_returnMsgSecond isEnd:YES];
            }
        }
    }
}


#pragma mark - private
///操作外设
///添加外设
- (void)addPeripheral:(CBPeripheral *)peripheral{
    if (![_connectPeripherals containsObject:peripheral]) {
        [_connectPeripherals addObject:peripheral];
    }
}
///删除外设
- (void)deletePeripheral:(CBPeripheral *)peripheral{
    [_connectPeripherals removeObject:peripheral];
}

///断开所有外设的链接
- (void)cancelAllPeripheralsConnection {
    for (int i=0;i<_connectPeripherals.count;i++) {
        [centralManager cancelPeripheralConnection:_connectPeripherals[i]];
    }
}

///发送报文
- (void)peripheral:(CBPeripheral *)peripheral sendMessage:(NSString *)message forCharacteristic:(CBCharacteristic *)characteristic{
    NSInteger msgLength = message.length/40;
    for (NSInteger i = 0 ; i < msgLength; i++) {
       NSString *sonMsg = [message substringWithRange:NSMakeRange(i * 40, 40)];
        [self peripheral:peripheral writeValue:sonMsg  forCharacteristic:characteristic];
        NSLog(@"%ld -- %@",i,sonMsg);
    }
    NSInteger msgRemain = message.length%40;
    if (msgRemain > 0) {
        NSString *sonMsg = [message substringFromIndex:msgLength * 40];
        [self peripheral:peripheral writeValue:sonMsg forCharacteristic:characteristic];
        NSLog(@"剩余 --- %@",sonMsg);
    }
}

- (void)peripheral:(CBPeripheral *)peripheral writeValue:(NSString *)value forCharacteristic:(CBCharacteristic *)characteristic{
    NSData *data = [self stringToByte:value];
    if (!data) {
        return;
    }
    if (characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
        [peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
    }else{
        [peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
    }
}

/**********************数据解析**************************/
//转16进制
-(NSData*)stringToByte:(NSString*)string{
    NSString *hexString=[[string uppercaseString] stringByReplacingOccurrencesOfString:@" " withString:@""];
    if ([hexString length]%2!=0) {
        return nil;
    }
    Byte tempbyt[1]={0};
    NSMutableData* bytes=[NSMutableData data];
    for(int i=0;i<[hexString length];i++)
    {
        unichar hex_char1 = [hexString characterAtIndex:i]; ////两位16进制数中的第一位(高位*16)
        int int_ch1;
        if(hex_char1 >= '0' && hex_char1 <='9')
            int_ch1 = (hex_char1-48)*16;   //// 0 的Ascll - 48
        else if(hex_char1 >= 'A' && hex_char1 <='F')
            int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65
        else
            return nil;
        i++;
        
        unichar hex_char2 = [hexString characterAtIndex:i]; ///两位16进制数中的第二位(低位)
        int int_ch2;
        if(hex_char2 >= '0' && hex_char2 <='9')
            int_ch2 = (hex_char2-48); //// 0 的Ascll - 48
        else if(hex_char2 >= 'A' && hex_char2 <='F')
            int_ch2 = hex_char2-55; //// A 的Ascll - 65
        else
            return nil;
        tempbyt[0] = int_ch1+int_ch2;  ///将转化后的数放入Byte数组里
        [bytes appendBytes:tempbyt length:1];
    }
    return bytes;
}

/**
 *  data转16进制
 */
- (NSString *)convertDataToHexStr:(NSData *)data {
    if (!data || [data length] == 0) {
        return @"";
    }
    NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
    [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
        unsigned char *dataBytes = (unsigned char*)bytes;
        for (NSInteger i = 0; i < byteRange.length; i++) {
            NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
            if ([hexStr length] == 2) {
                [string appendString:hexStr];
            } else {
                [string appendFormat:@"0%@", hexStr];
            }
        }
    }];
    return string;
}



@end
